package xxl.robotlegs.extensions.commandcenter.impl 
{
	import xxl.robotlegs.extensions.commandcenter.api.ICommandExecutor;
	import xxl.robotlegs.extensions.commandcenter.api.ICommandMapping;
	import xxl.robotlegs.extensions.commandcenter.FunctionApplyUtil;
	import xxl.robotlegs.framework.api.IInjector;
	import xxl.robotlegs.utils.GuardUtil;
	import xxl.robotlegs.utils.HookUtil;
	/**
	 * ...
	 * @author XXL
	 */
	public class CommandExecutor implements ICommandExecutor
	{
		private var _injector:IInjector;
		private var _removeMapping:Function;
		private var _handleResult:Function;
		
		private var _hasOverride:Boolean = false;
		
		public function CommandExecutor(injector:IInjector, removeMapping:Function = null, handleResult:Function = null) 
		{
			_injector = injector.createChild();
			_removeMapping = removeMapping;
			_handleResult = handleResult;
		}
		
		private function mapPayload(payload:CommandPayload):void
		{
			_hasOverride = false;
			const len:int = payload.length;
			for (var i:int = 0; i < len; i++) 
			{
				if (_injector.hasDirectMapping(payload.classes[i]))
				{
					_hasOverride = true;
				}
				else
				{
					_injector.map(payload.classes[i]).toValue(payload.values[i]);
				}
			}
			if (_hasOverride)
			{
				for (var j:int = 0; j < len; j++)
				{
					_injector.map(payload.classes[j], j.toString()).toValue(payload.values[j]);
				}
			}
		}
		
		private function unmapPayload(payload:CommandPayload):void
		{
			const len:int = payload.length;
			for (var i:int = 0; i < len; i++) 
			{
				if (_hasOverride && !_injector.hasDirectMapping(payload.classes[i]))
				{
					continue;
				}
				_injector.unmap(payload.classes[i]);
			}
			if (_hasOverride)
			{
				for (var j:int = 0; j < len; j++)
				{
					_injector.unmap(payload.classes[j], j.toString());
				}
			}
		}
		
		/* INTERFACE xxl.robotlegs.extensions.commandcenter.api.ICommandExecutor */
		
		public function executeCommand(mapping:ICommandMapping, payload:CommandPayload = null):void 
		{
			const hasPayload:Boolean = payload && payload.hasPayload();
			const injectionEnabled:Boolean = hasPayload && mapping.payloadInjectionEnabled;
			var command:Object = null;
			
			injectionEnabled && mapPayload(payload);
			
			if (mapping.guards.length == 0 || GuardUtil.GuardsApprove(mapping.guards, _injector))
			{
				const commandClass:Class = mapping.commandClass;
				mapping.fireOnce && _removeMapping && _removeMapping(mapping);
				command = _injector.getOrCreateNewInstance(commandClass);
				if (mapping.hooks.length > 0)
				{
					_injector.map(commandClass).toValue(command);
					HookUtil.ApplyHooks(mapping.hooks, _injector);
					_injector.unmap(commandClass);
				}
			}
			
			injectionEnabled && unmapPayload(payload);
			
			if (command && mapping.executeMethod)
			{
				const params:Array = hasPayload ? payload.values : [];
				const result:* = FunctionApplyUtil.Apply(command, mapping.executeMethod, params);
				_handleResult && _handleResult(result, command, mapping);
			}
		}
		
		public function executeCommands(mappings:Vector.<ICommandMapping>, payload:CommandPayload = null):void 
		{
			const len:int = mappings.length;
			for (var i:int = 0; i < len; i++) 
			{
				executeCommand(mappings[i], payload);
			}
		}
		
	}

}